/*
 * Copyright 2009 Brett Morgan.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.domesticmouse.waveoteditor.client.ops;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;


import com.domesticmouse.waveoteditor.client.wave.model.document.operation.AnnotationBoundaryMap;
import com.domesticmouse.waveoteditor.client.wave.model.document.operation.Attributes;
import com.domesticmouse.waveoteditor.client.wave.model.document.operation.AttributesUpdate;
import com.domesticmouse.waveoteditor.client.wave.model.document.operation.BufferedDocOp;
import com.domesticmouse.waveoteditor.client.wave.model.document.operation.DocOpCursor;
import com.domesticmouse.waveoteditor.client.wave.model.document.operation.impl.DocOpBuilder;

/**
 * A class to enable GWT-RPC to deliver {@link BufferedDocOp}s across the wire.
 * 
 * @author brettmorgan
 * 
 */

@SuppressWarnings("serial")
public class GwtClientDocOp implements Serializable {

	private ArrayList<DocOpComponent> components;

	public ArrayList<DocOpComponent> getComponents() {
		return components;
	}

	public void setComponents(ArrayList<DocOpComponent> components) {
		this.components = components;
	}

	public BufferedDocOp buildBufferedDocOp() {
		DocOpBuilder builder = new DocOpBuilder();

		for (DocOpComponent component : components) {
			component.build(builder);
		}
		return builder.build();
	}

	public void configureFromBufferedDocOp(BufferedDocOp bdo) {
		components = new ArrayList<DocOpComponent>();
		if (null != bdo) {
			bdo.apply(new DocOpCursor() {

				@Override
				public void retain(int itemCount) {
					RetainDocOpComponent retain = new RetainDocOpComponent();
					retain.setCount(itemCount);
					components.add(retain);
				}

				@Override
				public void characters(String chars) {
					CharactersDocOpComponent characters = new CharactersDocOpComponent();
					characters.setCharacters(chars);
					components.add(characters);
				}

				@Override
				public void deleteCharacters(String chars) {
					DeleteDocOpComponent deleteCharacters = new DeleteDocOpComponent();
					deleteCharacters.setDeletedCharacters(chars);
					components.add(deleteCharacters);
				}

				@Override
				public void elementStart(String type, Attributes attrs) {
					ElementStartDocOpComponent elementStart = new ElementStartDocOpComponent();
					elementStart.setType(type);

					HashMap<String, String> attributes = new HashMap<String, String>();

					for (Entry<String, String> entry : attrs.entrySet()) {
						attributes.put(entry.getKey(), entry.getValue());
					}
					elementStart.setAttributes(attributes);
					components.add(elementStart);
				}

				@Override
				public void elementEnd() {
					components.add(new ElementEndDocOpComponent());
				}

				@Override
				public void deleteElementStart(String type, Attributes attrs) {
					DeleteElementStartDocOpComponent deleteElementStart = new DeleteElementStartDocOpComponent();
					deleteElementStart.setType(type);

					HashMap<String, String> attributes = new HashMap<String, String>();

					for (Entry<String, String> entry : attrs.entrySet()) {
						attributes.put(entry.getKey(), entry.getValue());
					}
					deleteElementStart.setAttributes(attributes);
					components.add(deleteElementStart);
				}

				@Override
				public void deleteElementEnd() {
					components.add(new DeleteElementEndDocOpComponent());
				}

				@Override
				public void updateAttributes(AttributesUpdate attrUpdate) {
					UpdateAttributesDocOpComponent updateAttributes = new UpdateAttributesDocOpComponent();

					int changeSize = attrUpdate.changeSize();
					for (int changeIndex = 0; changeIndex < changeSize; changeIndex++) {
						updateAttributes.addUpdate(attrUpdate.getChangeKey(changeIndex), attrUpdate.getOldValue(changeIndex), attrUpdate
								.getNewValue(changeIndex));
					}

					components.add(updateAttributes);
				}

				// Attribute replacement not supported
				@Override
				public void replaceAttributes(Attributes oldAttrs, Attributes newAttrs) {
					throw new IllegalArgumentException("Attribute replacement not supported");
				}

				// Annotations not supported
				@Override
				public void annotationBoundary(AnnotationBoundaryMap map) {
					throw new IllegalArgumentException("Annotations not supported");
				}

			});
		}
	}

}
